package org.dromara.business.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.business.config.Web3jConfig;
import org.dromara.business.domain.vo.TransactionInfoVO;
import org.dromara.business.service.BlockchainService;
import org.dromara.business.service.RetryableWeb3jService;
import org.dromara.common.core.exception.ServiceException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.response.*;
import org.web3j.utils.Convert;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;
import java.util.Optional;

@Slf4j
@Service
@RequiredArgsConstructor
public class BlockchainServiceImpl implements BlockchainService {

    private final Web3j web3j;
    private final Web3jConfig web3jConfig;
    private final RetryableWeb3jService retryableWeb3jService;

    @Override
    public TransactionInfoVO getTransactionByHash(String txHash) {
        try {
            Optional<Transaction> transactionOpt = retryableWeb3jService.getTransactionByHash(txHash);
            
            if (transactionOpt.isPresent()) {
                Transaction transaction = transactionOpt.get();
                return convertToTransactionVO(transaction, null);
            } else {
                throw new ServiceException("交易不存在或查询失败: " + txHash);
            }
        } catch (IOException e) {
            log.error("查询交易失败: {}", txHash, e);
            throw new ServiceException("查询交易失败: " + e.getMessage());
        }
    }

    @Override
    public TransactionInfoVO getTransactionWithReceipt(String txHash) {
        try {
            // 使用并发方式同时获取交易和回执信息
            java.util.concurrent.CompletableFuture<Optional<Transaction>> transactionFuture = 
                retryableWeb3jService.getTransactionByHashAsync(txHash);
            java.util.concurrent.CompletableFuture<Optional<TransactionReceipt>> receiptFuture = 
                retryableWeb3jService.getTransactionReceiptAsync(txHash);
            
            Optional<Transaction> transactionOpt = transactionFuture.get();
            Optional<TransactionReceipt> receiptOpt = receiptFuture.get();

            if (transactionOpt.isPresent()) {
                Transaction transaction = transactionOpt.get();
                TransactionReceipt receipt = receiptOpt.orElse(null);
                return convertToTransactionVO(transaction, receipt);
            } else {
                throw new ServiceException("交易不存在或查询失败: " + txHash);
            }
        } catch (Exception e) {
            log.error("查询交易详情失败: {}", txHash, e);
            throw new ServiceException("查询交易详情失败: " + e.getMessage());
        }
    }

//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public BlockchainTransaction saveTransaction(TransactionRequest request) {
//        TransactionInfoVO txInfo = getTransactionWithReceipt(request.getTxHash());
//
//        LambdaQueryWrapper<BlockchainTransaction> wrapper = new LambdaQueryWrapper<>();
//        wrapper.eq(BlockchainTransaction::getTxHash, request.getTxHash());
//        BlockchainTransaction existingTx = getOne(wrapper);
//
//        if (existingTx != null) {
//            log.info("交易已存在: {}", request.getTxHash());
//            return existingTx;
//        }
//
//        BlockchainTransaction blockchainTx = new BlockchainTransaction();
//        blockchainTx.setUserId(request.getUserId());
//        blockchainTx.setStakingId(request.getStakingId());
//        blockchainTx.setTxHash(txInfo.getTxHash());
//        blockchainTx.setFromAddress(txInfo.getFromAddress());
//        blockchainTx.setToAddress(txInfo.getToAddress());
//        blockchainTx.setValue(txInfo.getValue());
//        blockchainTx.setValueDecimal(txInfo.getValueInEther());
//        blockchainTx.setGasPrice(txInfo.getGasPrice());
//        blockchainTx.setGasUsed(txInfo.getGasUsed());
//        blockchainTx.setGasFee(txInfo.getGasFee());
//        blockchainTx.setBlockNumber(txInfo.getBlockNumber());
//        blockchainTx.setBlockHash(txInfo.getBlockHash());
//        blockchainTx.setTxIndex(txInfo.getTxIndex());
//        blockchainTx.setNonce(txInfo.getNonce());
//        blockchainTx.setInputData(txInfo.getInputData());
//        blockchainTx.setContractAddress(txInfo.getContractAddress());
//        blockchainTx.setNetwork(web3jConfig.getNetworkName());
//        blockchainTx.setStatus(txInfo.getStatus());
//        blockchainTx.setConfirmationCount(txInfo.getConfirmations());
//        blockchainTx.setTxType(txInfo.getTxType());
//        blockchainTx.setTxTime(txInfo.getTxTime());
//
//        save(blockchainTx);
//
//        log.info("保存交易记录成功: {}", request.getTxHash());
//        return blockchainTx;
//    }
//
//    @Override
//    public List<BlockchainTransaction> getUserTransactions(Long userId) {
//        LambdaQueryWrapper<BlockchainTransaction> wrapper = new LambdaQueryWrapper<>();
//        wrapper.eq(BlockchainTransaction::getUserId, userId)
//                .orderByDesc(BlockchainTransaction::getCreateTime);
//        return list(wrapper);
//    }
//
//    @Override
//    public List<BlockchainTransaction> getStakingTransactions(Long stakingId) {
//        LambdaQueryWrapper<BlockchainTransaction> wrapper = new LambdaQueryWrapper<>();
//        wrapper.eq(BlockchainTransaction::getStakingId, stakingId)
//                .orderByDesc(BlockchainTransaction::getCreateTime);
//        return list(wrapper);
//    }

    @Override
    public BigDecimal getEthBalance(String address) {
        try {
            BigInteger wei = retryableWeb3jService.getBalance(address);
            return Convert.fromWei(new BigDecimal(wei), Convert.Unit.ETHER);
        } catch (IOException e) {
            log.error("查询余额失败: {}", address, e);
            throw new ServiceException("查询余额失败: " + e.getMessage());
        }
    }

    @Override
    public Long getBlockNumber() {
        try {
            BigInteger blockNumber = retryableWeb3jService.getBlockNumber();
            return blockNumber.longValue();
        } catch (IOException e) {
            log.error("查询区块高度失败", e);
            throw new ServiceException("查询区块高度失败: " + e.getMessage());
        }
    }

    @Override
    public String getNetworkInfo() {
        try {
            String clientVersion = retryableWeb3jService.getClientVersion();
            Long blockNumber = getBlockNumber();

            return String.format("Network: %s, Client: %s, Block Height: %d",
                    web3jConfig.getNetworkName(),
                    clientVersion,
                    blockNumber);
        } catch (IOException e) {
            log.error("查询网络信息失败", e);
            throw new ServiceException("查询网络信息失败: " + e.getMessage());
        }
    }

    @Override
    public Boolean isContractAddress(String address) {
        try {
            String code = retryableWeb3jService.getCode(address);
            return code != null && !code.equals("0x");
        } catch (IOException e) {
            log.error("检查合约地址失败: {}", address, e);
            return false;
        }
    }

    @Override
    public Integer getTransactionConfirmations(String txHash) {
        try {
            Optional<TransactionReceipt> receiptOpt = retryableWeb3jService.getTransactionReceipt(txHash);
            if (receiptOpt.isPresent()) {
                TransactionReceipt receipt = receiptOpt.get();
                Long txBlockNumber = Long.parseLong(receipt.getBlockNumber().toString(), 16);
                Long currentBlockNumber = getBlockNumber();
                return (int)(currentBlockNumber - txBlockNumber);
            }
            return 0;
        } catch (Exception e) {
            log.error("查询交易确认数失败: {}", txHash, e);
            return 0;
        }
    }

    private TransactionInfoVO convertToTransactionVO(Transaction tx, TransactionReceipt receipt) {
        TransactionInfoVO.TransactionInfoVOBuilder builder = TransactionInfoVO.builder();

        builder.txHash(tx.getHash());
        builder.fromAddress(tx.getFrom());
        builder.toAddress(tx.getTo());
        builder.value(tx.getValue().toString());

        BigDecimal valueInWei = new BigDecimal(tx.getValue());
        BigDecimal valueInEther = Convert.fromWei(valueInWei, Convert.Unit.ETHER);
        builder.valueInEther(valueInEther);

        builder.gasPrice(tx.getGasPrice().toString());
        builder.nonce(tx.getNonce().longValue());
        builder.inputData(tx.getInput());

        if (tx.getTo() != null) {
            builder.isContract(isContractAddress(tx.getTo()));
        } else {
            builder.isContract(false);
        }

        if (tx.getInput() != null && tx.getInput().length() >= 10) {
            builder.methodId(tx.getInput().substring(0, 10));
        }

        if (receipt != null) {
            builder.gasUsed(receipt.getGasUsed().toString());

            BigDecimal gasPriceWei = new BigDecimal(tx.getGasPrice());
            BigDecimal gasUsed = new BigDecimal(receipt.getGasUsed());
            BigDecimal gasFeeWei = gasPriceWei.multiply(gasUsed);
            BigDecimal gasFeeEther = Convert.fromWei(gasFeeWei, Convert.Unit.ETHER);
            builder.gasFee(gasFeeEther);

            builder.blockNumber(Long.parseLong(receipt.getBlockNumber().toString(), 16));
            builder.blockHash(receipt.getBlockHash());
            builder.txIndex(Integer.parseInt(receipt.getTransactionIndex().toString(), 16));
            builder.contractAddress(receipt.getContractAddress());

            String status = receipt.getStatus();
            if ("0x1".equals(status)) {
                builder.status(1);
                builder.statusDesc("成功");
            } else {
                builder.status(0);
                builder.statusDesc("失败");
                if (receipt.getRevertReason() != null) {
                    builder.errorMessage(receipt.getRevertReason());
                }
            }

            builder.confirmations(getTransactionConfirmations(tx.getHash()));

            try {
                EthBlock block = web3j.ethGetBlockByHash(receipt.getBlockHash(), false).send();
                if (block.getBlock() != null) {
                    long timestamp = block.getBlock().getTimestamp().longValue();
                    LocalDateTime txTime = LocalDateTime.ofInstant(
                            Instant.ofEpochSecond(timestamp), ZoneId.systemDefault());
                    builder.txTime(txTime);
                }
            } catch (IOException e) {
                log.error("查询区块时间失败", e);
            }
        } else {
            builder.status(-1);
            builder.statusDesc("待确认");
            builder.confirmations(0);
        }

        builder.network(web3jConfig.getNetworkName());

        if (tx.getTo() == null) {
            builder.txType("合约创建");
        } else if (tx.getInput() != null && !"0x".equals(tx.getInput())) {
            builder.txType("合约调用");
        } else {
            builder.txType("ETH转账");
        }

        return builder.build();
    }
}
